package service

import (
	"context"
	"errors"
	"webook/internal/domain"
	"webook/internal/repository"
)

//go:generate mockgen -source=./article.go -package=svcmocks -destination=mock/article.mock.go
type IArticleService interface {
	Edit(ctx context.Context, article domain.Article) (int64, error)
	Publish(ctx context.Context, article domain.Article) (int64, error)
	List(ctx context.Context, authorId int64, offset int, limit int) ([]domain.Article, error)
	ListPub(ctx context.Context, offset int, limit int) ([]domain.Article, error)
	Detail(ctx context.Context, authorId int64, articleId int64) (domain.Article, error)
	PubDetail(ctx context.Context, articleId int64) (domain.PublishArticle, error)
}

type ArticleService struct {
	repo       repository.ArticleRepository
	authorRepo repository.ArticleAuthorRepository
	readerRepo repository.ArticleReaderRepository
}

func (svc *ArticleService) ListPub(ctx context.Context, offset int, limit int) ([]domain.Article, error) {
	return svc.repo.ListPub(ctx, offset, limit)
}

func NewArticleService(repo repository.ArticleRepository) IArticleService {
	return &ArticleService{
		repo: repo,
	}
}

func (svc *ArticleService) PubDetail(ctx context.Context, articleId int64) (domain.PublishArticle, error) {
	return svc.repo.GetPubById(ctx, articleId)
}

func (svc *ArticleService) Detail(ctx context.Context, authorId int64, articleId int64) (domain.Article, error) {
	return svc.repo.GetById(ctx, authorId, articleId)
}
func (svc *ArticleService) List(ctx context.Context, authorId int64, offset int, limit int) ([]domain.Article, error) {
	return svc.repo.List(ctx, authorId, offset, limit)
}

func (svc *ArticleService) Edit(ctx context.Context, article domain.Article) (int64, error) {
	if article.Id > 0 {
		// 说明是编辑后重新修改
		return svc.repo.Update(ctx, article)
	} else {
		// 说明是新帖子
		// 调用repo存储帖子
		return svc.repo.Create(ctx, article)
	}
}
func (svc *ArticleService) Publish(ctx context.Context, article domain.Article) (int64, error) {
	return svc.repo.Sync(ctx, article)
}

// PublishV1 在service层面进行处理
func (svc *ArticleService) PublishV1(ctx context.Context, article domain.Article) (int64, error) {
	// 1. 保存到制作库
	var (
		id  int64
		err error
	)
	if article.Id > 0 {
		id, err = svc.authorRepo.Update(ctx, article)
	} else {
		id, err = svc.authorRepo.Create(ctx, article)
	}
	if err != nil {
		return 0, err
	}
	// 2. 同步到线上库
	// 说明是以下两种情况之一
	// 1. 帖子之前没有发表过，然后发表
	// 2. 帖子已经发表过了，重新发表
	// 但是在业务层不做区分（其实是一个upsert的语法）
	// 保存制作库的id
	article.Id = id
	// 重试机制
	for i := 0; i < 3; i++ {
		save, err := svc.readerRepo.Save(ctx, article)
		if err == nil {
			return save, nil
		}
	}
	// 重试全部失败
	// TODO 记录日志，监控
	return 0, errors.New("保存到线上库失败，重试次数耗尽")
}
